package com.hk.core.authentication.api;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.hk.commons.util.ByteConstants;
import com.hk.commons.util.CollectionUtils;
import com.hk.commons.util.Constants;
import com.hk.commons.util.StringUtils;
import com.hk.core.authentication.api.enums.ThirdAccountType;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.builder.HashCodeBuilder;

import java.io.Serializable;
import java.math.BigInteger;
import java.security.Principal;
import java.time.LocalDate;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * 当前登陆的用户，所有获取当前登陆用户信息必须继承此类
 *
 * @author kevin
 * @date 2017年9月28日上午9:45:55
 */
@Getter
@Setter
@Accessors(chain = true)
@NoArgsConstructor
public class UserPrincipal implements Principal, Serializable {

    /**
     * 当前用户id
     */

    private Long userId;

    /**
     * 用户账号
     */
    private String account;

    /**
     * 真实名称
     */
    private String realName;

    /**
     * 用户类型
     */
    private Byte userType;

    /**
     * 用户手机区号
     */
    private Integer areaCode;

    /**
     * 用户手机号
     */
    private String mobileNumber;

    /**
     * 用户邮箱
     */
    private String email;

    /**
     * 用户性别
     */
    private Byte gender;

    /**
     * 用户头像
     */
    private String iconPath;

    /**
     * 用户数据权限值
     */
    private BigInteger dataRule;

    /**
     * 租戶信息
     */
    private TenantInfo tenantInfo;

    /**
     * 获取 app 信息，对于 单点登陆，或 oauth2 登陆时有效
     */
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private ClientAppInfo appInfo;

    /**
     * 用户所在机构id
     */
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Long orgId;

    /**
     * 用户所在机构名称
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private String orgName;

    /**
     * 用户所在部门
     */
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Set<DeptInfo> deptInfos;

    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private LocalDate birth;

    /**
     * 用户其它扩展信息
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private Map<String, String> additionalInfo;

    /**
     * 用户角色
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private Set<String> roles;

    /**
     * 用户权限
     */
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private Set<String> permissions;

    public UserPrincipal(Long userId, String account, Byte userType) {
        this.userId = userId;
        this.account = account;
        this.userType = userType;
    }

    /**
     * 如果是微信登陆的，获取微信登陆的unionId，公众号和小程序都支持
     */
    public final String getUnionId() {
        return getValue(Constants.UNION_ID);
    }

    /**
     * 如果是微信登陆的，获取微信登陆的 openId，公众号和小程序都支持
     */
    public final String getOpenId() {
        return getValue(Constants.OPEN_ID);
    }

    /**
     * 如果是微信小程序登陆的，获取微信小程序登陆的 sessionKey
     */
    public final String getSessionKey() {
        return getValue(Constants.SESSION_KEY);
    }

    /**
     * 获取用户其它信息
     *
     * @param key key名称
     */
    public final String getValue(String key) {
        Map<String, String> info = getAdditionalInfo();
        return Objects.nonNull(info) ? info.get(key) : null;
    }

    /**
     * 判断是否为微信公众号认证
     */
    @JsonIgnore
    public final boolean isWeiXinAccountAuthentication() {
        return StringUtils.hasLength(getValue(ThirdAccountType.WX_ACCOUNT.name()));
    }

    /**
     * 判断是否为微信公众号认证
     */
    @JsonIgnore
    public final boolean isWeiXinMPAuthentication() {
        return StringUtils.hasLength(getValue(ThirdAccountType.WX_MP.name()));
    }

    /**
     * 判断是否为支付宝认证
     */
    @JsonIgnore
    public final boolean isAliPayAuthentication() {
        return StringUtils.hasLength(getValue(ThirdAccountType.ALI.name()));
    }

    /**
     * 判断用户是否有权限
     *
     * @param permissionName 权限名
     */
    public final boolean hasPermission(String permissionName) {
        return isAdministrator() || CollectionUtils.contains(this.permissions, permissionName);
    }

    /**
     * 判断用户是否有角色
     *
     * @param roleName roleName
     */
    public final boolean hasRole(String roleName) {
        return isAdministrator() || CollectionUtils.contains(this.roles, roleName);
    }

    /**
     * 是否为管理员
     *
     * @return true if is an admin.
     */
    @JsonIgnore
    public final boolean isAdministrator() {
        return ByteConstants.ONE == userType;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 37)
                .append(getUserId())
                .append(getAccount())
                .toHashCode();
    }

    /**
     * <pre>
     * 这个方法的返回值作为唯一的用户，
     * 像websocket 一对一发消息时，会使用这个方法来判断发送给哪个用户，尽量和父类保持一致
     * </pre>
     */
    @Override
    @JsonIgnore
    public final String getName() {
        return getAccount();
    }

    @Override
    public boolean equals(Object obj) {
        return (obj instanceof UserPrincipal principal) && this.getAccount().equals(principal.getAccount());
    }

}
